package com.leyou.user.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.RegexUtils;
import com.leyou.user.dto.UserDTO;
import com.leyou.user.dto.UserRequestDTO;
import com.leyou.user.entity.User;
import com.leyou.user.mapper.UserMapper;
import com.leyou.user.service.UserService;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final AmqpTemplate amqpTemplate;
    private final StringRedisTemplate redisTemplate;
    private final BCryptPasswordEncoder passwordEncoder;
//    private final JwtUtils jwtUtils;

    public UserServiceImpl(AmqpTemplate amqpTemplate,
                           StringRedisTemplate redisTemplate,
                           BCryptPasswordEncoder passwordEncoder
//                           JwtUtils jwtUtils
                                            ) {
        this.amqpTemplate = amqpTemplate;
        this.redisTemplate = redisTemplate;
        this.passwordEncoder = passwordEncoder;
//        this.jwtUtils = jwtUtils;
    }

    private final String KEY_PREFIX_USER_VERIFY = "ly:user:verify:";

    @Override
    public void sendMsg(String phone) {

        if (StringUtils.isEmpty(phone) || !RegexUtils.isPhone(phone)) {
            throw new LyException(400, "手机号有误");
        }

        Map<String, String> msg = new HashMap<>();

        String code = RandomStringUtils.randomNumeric(6);

        msg.put("code", code);
        msg.put("phone", phone);

        amqpTemplate.convertAndSend("sms", "sms.verify", msg);

        String key = KEY_PREFIX_USER_VERIFY + phone;

        //在验证码处理时要考虑复用问题，适合使用redis
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);


    }

    @Override
    public Boolean checkExists(String data, Integer type) {

        if (StringUtils.isEmpty(data)) {
            throw new LyException(400, "检查数据不能为空");
        }


//        if (type!=1||type!=2){
//            throw new LyException(400,"验证类型有误");
//        }

        String colName;
        switch (type) {
            case 1:
                colName = "username";
                break;

            case 2:
                colName = "phone";
                break;
            default:
                throw new LyException(400, "验证类型有误");
        }
        //select count(id) from tb_user where username = #{ldh}
        return this.query().eq(colName, data).count() == 1;


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(UserRequestDTO userRequestDTO) {
        //校验验证码
        String code = userRequestDTO.getCode();
        if (StringUtils.isEmpty(code)) {
            throw new LyException(400, "验证码不能为空");
        }

        String key = KEY_PREFIX_USER_VERIFY + userRequestDTO.getPhone();

        if (!redisTemplate.hasKey(key) || !code.equals(redisTemplate.opsForValue().get(key))) {
            throw new LyException(400, "验证码已失效，或输入错误");
        }

        User user = userRequestDTO.toEntity(User.class);

        //对密码进行加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        //密码加密
        this.save(user);

        redisTemplate.delete(key);
    }

    @Override
    public UserDTO queryUserWithPass(String username, String password) {
        //如果涉及到加密问题，则一定不能直接代码密码的明文
        User user = this.query()
                .eq("username", username)
                .one();
        if (null == user) {
            throw new LyException(401, "用户名错误");
        }

        //验密过程，加非取反表示，密码错误，1，推测盐值，2，根据明文和盐值二次加密并且要比对加密结果和原始密文
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new LyException(401, "密码错误");
        }
        return new UserDTO(user);
    }
}
